#include <thread>

#ifdef XG_LINUX
#include <termios.h>
#endif

#include <http/HttpServer.h>

string GetAppCodeString();
string GetCgiCodeString();
string GetMainCodeString();
string GetMakefileString();
SmartBuffer GetSQLiteTemplate();
string GetConfigTemplateString();
string GetDBConfigTemplateString();
string GetMimeConfigTemplateString();

class HttpApplication : public Application
{
protected:
	Sharemem shm;
	HttpServer* svr;

	void printTips() const
	{
		puts(" webserver command");
		puts("-----------------------------------------");
		puts(" -l    : log monitor");
		puts(" -k    : stop webserver");
		puts(" -u    : update configure");
		puts(" -r    : reload webserver");
		puts(" -s    : restart webserver");
		puts(" -dir  : create project directory");
		puts(" -etc  : export template configure");
		puts(" -init : initialize webserver configure");
		puts("-----------------------------------------");
		puts("");
	}

public:
	HttpApplication() : svr(HttpServer::Instance()){}

	void clean()
	{
		LogThread::Instance()->wait();

		svr->initSystem(true);

		shm.close();
	}
	bool commit(const char* host, int port)
	{
		Socket sock;
		char cmd = 0;

		CHECK_FALSE_RETURN(sock.init());
		CHECK_FALSE_RETURN(sock.connect(host, port));
		CHECK_FALSE_RETURN(sock.writeObject(cmd));

		sock.close();

		return true;
	}
	bool main()
	{
		const char* path = NULL;

		if (GetCmdParam("?") || GetCmdParam("--help"))
		{
			printTips();

			return true;
		}

		if ((path = GetCmdParam("-etc")))
		{
			if (*path)
			{
				if (GetCmdParam("-i") && path::size(path) > 0)
				{
					CHECK_FALSE_RETURN(cmdx::CheckCommand("file[%s] exists, overwrite or not ? (y/n)", path::name(path).c_str()));
				}

				TextFile log;

				if (log.open(path, true))
				{
					log.puts(GetConfigTemplateString());

					puts("export template config success");
				}
				else
				{
					puts("export template config failed");
				}
			}
			else
			{
				puts(GetConfigTemplateString().c_str());
			}

			return true;
		}

		if ((path = GetCmdParam("-dir")))
		{
			if (*path == 0)
			{
				puts("please input project directory");

				return false;
			}

			if (path::type(path) >= ePATH)
			{
				puts("project directory already exist");

				return false;
			}

			auto create = [&](){
				CHECK_FALSE_RETURN(path::mkdir(path));
				CHECK_FALSE_RETURN(Process::SetCurrentDirectory(path));

				XFile file;
				string data;

				path::mkdir("pub");
				path::mkdir("css");
				path::mkdir("cgi/app");
				path::mkdir("cgi/cgi");
				path::mkdir("cgi/dao");

				CHECK_FALSE_RETURN(file.create("cgi/makefile"));
				data = GetMakefileString();
				CHECK_FALSE_RETURN(file.write(data.c_str(), data.length()) > 0);

				CHECK_FALSE_RETURN(file.create("cgi/main.cpp"));
				data = GetMainCodeString();
				CHECK_FALSE_RETURN(file.write(data.c_str(), data.length()) > 0);

				CHECK_FALSE_RETURN(file.create("cgi/app/apptest.cpp"));
				data = GetAppCodeString();
				CHECK_FALSE_RETURN(file.write(data.c_str(), data.length()) > 0);

				CHECK_FALSE_RETURN(file.create("cgi/cgi/cgitest.cgi"));
				data = GetCgiCodeString();
				CHECK_FALSE_RETURN(file.write(data.c_str(), data.length()) > 0);

				return true;
			};

			if (create())
			{
				puts("create project success");
			}
			else
			{
				puts("create project failed");
			}

			return true;
		}

		if ((path = GetCmdParam("-init")))
		{
			auto create = [&](){
				if (*path)
				{
					CHECK_FALSE_RETURN(path::mkdir(path));
					CHECK_FALSE_RETURN(Process::SetCurrentDirectory(path));
				}

				if (path::type("sqlte.db") >= ePATH
					|| path::type("config.lua") >= ePATH
					|| path::type("dbconfig.lua") >= ePATH
					|| path::type("mimeconfig.lua") >= ePATH)
				{
					CHECK_FALSE_RETURN(cmdx::CheckCommand("configure already exist, overwrite or not ? (y/n)"));
				}

				string time = DateTime::GetBizId().substr(0, 14);

				if (path::type("sqlite.db") >= ePATH)
				{
					path::rename("sqlite.db", stdx::format("sqlite.db.%s", time.c_str()));
				}

				if (path::type("config.lua") >= ePATH)
				{
					path::rename("config.lua", stdx::format("config.lua.%s", time.c_str()));
				}

				if (path::type("dbconfig.lua") >= ePATH)
				{
					path::rename("dbconfig.lua", stdx::format("dbconfig.lua.%s", time.c_str()));
				}

				if (path::type("mimeconfig.lua") >= ePATH)
				{
					path::rename("mimeconfig.lua", stdx::format("mimeconfig.lua.%s", time.c_str()));
				}

				File file;
				string data;
				SmartBuffer buffer;

				CHECK_FALSE_RETURN(file.open("sqlite.db", "wb+"));
				CHECK_FALSE_RETURN((buffer = GetSQLiteTemplate()).size() > 0);
				CHECK_FALSE_RETURN(file.write(buffer.str(), buffer.size()) > 0);

				CHECK_FALSE_RETURN(file.open("config.lua", "w+"));
				data = GetConfigTemplateString();
				CHECK_FALSE_RETURN(file.write(data.c_str(), data.length()) > 0);

				CHECK_FALSE_RETURN(file.open("dbconfig.lua", "w+"));
				data = GetDBConfigTemplateString();
				CHECK_FALSE_RETURN(file.write(data.c_str(), data.length()) > 0);

				CHECK_FALSE_RETURN(file.open("mimeconfig.lua", "w+"));
				data = GetMimeConfigTemplateString();
				CHECK_FALSE_RETURN(file.write(data.c_str(), data.length()) > 0);

				return true;
			};

			if (create())
			{
				puts("initialize configure success");
			}
			else
			{
				puts("initialize configure success");
			}

			return true;
		}

		path = GetCmdParam(1);

		if (path == NULL || path::type(path) < eFILE)
		{
			HttpServer::ShareData* shd = HttpServer::Instance()->getShareData();

			if (shd)
			{
				char* cmd = shd->data;

				if (*cmd == 'r')
				{
					puts("reloading webserver ...");

					return false;
				}

				int port = shd->port;
				const char* host = shd->host;

				if (strcmp(host, HOST_IP) == 0) host = LOCAL_IP;

				if (GetCmdParam("-restart") == NULL && commit(host, port))
				{
					auto command = [&](char flag){
						int num = 100;

						cmd[0] = flag;
						cmd[1] = '-';

						commit(host, port);

						while (num-- > 0 && cmd[1] == '-') Sleep(100);
					};

					if (GetCmdParam("-k"))
					{
						command('k');

						if (cmd[1] == 0)
						{
							puts("stop webserver success");
						}
						else
						{
							puts("stop webserver failed");
						}
					}
					else if (GetCmdParam("-s"))
					{
						command('s');

						if (cmd[1] == 0)
						{
							puts("restart webserver success");
						}
						else
						{
							puts("restart webserver failed");
						}
					}
					else if (GetCmdParam("-r"))
					{
						command('r');

						if (cmd[1] == 0)
						{
							puts("reload webserver success");
						}
						else if (cmd[1] == 1)
						{
							puts("reload webserver failed");
						}
						else
						{
							puts("reloading webserver ...");
						}
					}
					else if (GetCmdParam("-u"))
					{
						HttpRequest request("execmodule");

						request.setParameter("cmd", "sync");

						sp<HttpResponse> response = HttpServer::Instance()->getLocaleResult(request);

						if (response)
						{
							SmartBuffer data = response->getResult();

							if (data.isNull() || strstr(data.str(), "\"code\":-")) response = NULL;
						}

						if (response)
						{
							puts("update configure success");
						}
						else
						{
							puts("update configure failed");
						}
					}
					else if (GetCmdParam("-l"))
					{
						MemQueue mq;
						Semaphore sem;

						if (sem.open(HttpServer::GetSemaphoreName()))
						{
							if (sem.wait())
							{
								mq.open(shd->logdata);

								sem.release();
								
								std::thread([&](){
									while (true)
									{
										int num = 3;

										while (--num >= 0)
										{
											sleep(3);

											if (Socket().connect(host, port)) break;
										}

										if (num < 0) ErrorExit(0);
									}
								}).detach();
								
								command('l');
								
								while (true)
								{
									if (sem.wait())
									{
										int hdrsz = 32;
										SmartBuffer data = mq.pop();

										sem.release();

										if (data.isNull())
										{
											Sleep(10);

											continue;
										}

										string msg = stdx::syscode(data.str());
										string head(data.str(), data.str() + stdx::minval(data.size(), hdrsz));

										if (head.find("[ERR]") != string::npos)
										{
											SetConsoleTextColor(eRED);

											fwrite(msg.c_str(), msg.length(), 1, stdout);

											SetConsoleTextColor(eWHITE);
										}
										else if (head.find("[IMP]") != string::npos)
										{
											SetConsoleTextColor(eYELLOW);

											fwrite(msg.c_str(), msg.length(), 1, stdout);

											SetConsoleTextColor(eWHITE);
										}
										else
										{
											fwrite(msg.c_str(), msg.length(), 1, stdout);
										}

										fflush(stdout);
									}
								}
							}
						}
					}
					else
					{
						puts("undefined command");

						return false;
					}
					
					return true;
				}
			}
		}

		if (path == NULL || *path == 0)
		{
			puts("please input configure filename");
	
			return false;
		}

		if (GetCmdParam("-l")) LogThread::Instance()->setLogFlag(2);
	
		if (HttpServer::Instance()->init(path))
		{
			string path = HttpServer::Instance()->getPath();

			path::mkdir(path + "dat/pub");

			LogTrace(eINF, "enter process loop ...");

			Process::SetCommonExitSignal();

#ifdef XG_LINUX
			std::thread([](){
				struct termios tm;

				while (tcgetattr(STDIN_FILENO, &tm) >= 0) sleep(1);

				close(STDIN_FILENO);
				close(STDOUT_FILENO);
				close(STDERR_FILENO);
			}).detach();
#endif
			HttpServer::Instance()->loop();
		}
		else
		{
			LogTrace(eERR, "initialize server failed");
		}
		
		LogTrace(eERR, "process exiting ...");
	
		clean();
		
		return false;
	}
};

START_APP(HttpApplication)
